<?php
/**
 * Author: DefinitlyEvil
 * Created at: 2020/4/14 19:18
 */

namespace App\PublicAPI;


use App\Controller;
use App\Tools\Encryption;
use App\Tools\Pattern;

class AuthController extends Controller
{
    const PATTERN_USERNAME = '/^[A-Za-z0-9_]{3,15}$/';

    public $needAuth = false;

    public function checkUsername($params = []) {
        if(!isset($params['u']) or !Pattern::matches(self::PATTERN_USERNAME, $params['u'])) {
            return ['invalid params', false];
        }
        $pdo = $this->getPdo();
        $stm = $pdo->prepare('SELECT `id` FROM `accounts` WHERE `username`=:u');
        $stm->bindParam(':u', $params['u']);
        if($stm->execute() === false) ['database error', false];
        $available = $stm->fetch(\PDO::FETCH_ASSOC) === false;
        return [['available' => $available], true];
    }

    public function login($params = []) {
        if(!isset($params['u']) or !isset($params['p']) or !Pattern::matches(self::PATTERN_USERNAME, $params['u'])) {
            return ['invalid params', false];
        }
        $pdo = $this->getPdo();
        $stm = $pdo->prepare('SELECT * FROM `accounts` WHERE `username`=:u AND `password`=:p');
        $stm->bindParam(':u', $params['u']);
        $stm->bindParam(':p', $params['p']);
        if($stm->execute() === false) return ['database error', false];
        $user = $stm->fetch(\PDO::FETCH_ASSOC);
        if($user === false) return ['invalid credentials', false];

        $token = Encryption::generate_token($user);

        return [['id' => $user['id'], 'uuid' => $user['uuid'], 'username' => $user['username'], 'token' => $token[0], 'expire' => $token[1]], true];
    }

    public function register($params = []) {
        if(!isset($params['u']) or !isset($params['p']) or !is_string($params['u']) or !is_string($params['p']) or !isset($params['ph']) or !Pattern::matches('/^1[0-9]{10}$/', $params['ph']) or !Pattern::matches(self::PATTERN_USERNAME, $params['u']) or !is_string($params['p']) or !isset($params['sms']) or !is_int($params['sms']) or !isset($params['smsCode']) or !Pattern::matches('/^[0-9]{6}$/', $params['smsCode'])) {
            return ['invalid params', false];
        }
        $u = &$params['u'];
        $p = &$params['p'];
        $phone = &$params['ph'];
        $sms = &$params['sms'];
        $smsCode = &$params['smsCode'];

        // check code integrity
        {
            $sum5 = 0;
            for($i = 0; $i < 5; $i++) {
                $smsDigit = intval(substr($smsCode, $i, 1));
                $sum5 += $smsDigit;
            }
            $smsLastCodeCorrect = 10-((($sum5+3)%7)+1);
            if(intval(substr($smsCode, 5, 1)) !== $smsLastCodeCorrect) {
                return ['invalid sms code', false];
            }
        }
        // check code from database
        {
            $stm = $this->getPdo()->prepare(
                'UPDATE `phone_codes` SET `used`=1 WHERE `id`=:sms AND `phone`=:ph AND `code`=:code'
            );
            $stm->bindParam(':sms', $sms, \PDO::PARAM_INT);
            $stm->bindParam(':ph', $phone, \PDO::PARAM_STR);
            $stm->bindParam(':code', $smsCode, \PDO::PARAM_STR);
            if($stm->execute() === false) return ['database error', false];
            if($stm->rowCount() <= 0) return ['invalid sms code', false];
        }

        $uuid = sprintf('%s-%s-%s-%s-%s', bin2hex(random_bytes(4)),bin2hex(random_bytes(2)),bin2hex(random_bytes(2)),bin2hex(random_bytes(2)),bin2hex(random_bytes(6)));

        // insert into database
        $stm = $this->getPdo()->prepare(
            'INSERT INTO `accounts` (`username`,`phone`,`uuid`,`password`) VALUES(:u,:ph,:uuid,:p)'
        );
        $stm->bindParam(':u', $u);
        $stm->bindParam(':p', $p);
        $stm->bindParam(':ph', $phone);
        $stm->bindParam(':uuid', $uuid);

        if($stm->execute() === false) return ['register failed, database error / 1', false];
        if($stm->rowCount() <= 0) return ['register failed, database error / 2', false];

        $user_id = intval($this->getPdo()->lastInsertId());

        $stm = $this->getPdo()->prepare( <<< EOL
INSERT INTO `item_storage` (`account_id`,`item_type_id`,`equipped`,`create_time`,`expire_time`) 
VALUES
  (:acc,:item1,0,UNIX_TIMESTAMP(),:exp1),
  (:acc,:item2,0,UNIX_TIMESTAMP(),:exp2),
  (:acc,:item3,0,UNIX_TIMESTAMP(),:exp3),
  (:acc,:item4,0,UNIX_TIMESTAMP(),:exp4)
EOL
);
        $stm->bindParam(':acc', $user_id, \PDO::PARAM_INT);
        $stm->bindValue(':item1', 1, \PDO::PARAM_INT);
        $stm->bindValue(':exp1', null, \PDO::PARAM_INT);
        $stm->bindValue(':item2', 2, \PDO::PARAM_INT);
        $stm->bindValue(':exp2', time()+604800, \PDO::PARAM_INT);
        $stm->bindValue(':item3', 9, \PDO::PARAM_INT);
        $stm->bindValue(':exp3', null, \PDO::PARAM_INT);
        $stm->bindValue(':item4', 10, \PDO::PARAM_INT);
        $stm->bindValue(':exp4', time()+(86400*7), \PDO::PARAM_INT);
        $stm->execute();


        return true;
    }
}